﻿using System;
using System.Collections.Generic;
using System.Linq;
using IQIGame.Onigao.Framework;
using IQIGame.Onigao.Game;
using IQIGame.Onigao.GamePlay;
using LFloatMath.Math;
using NetProtocol;
using NetProtocol.POD;

namespace IQIGame.Onigao.Logic
{
    public class BattleTurnQueueController : TController
    {
        private BattlePlayController m_PlayCtrl;
        private int RealRound => this.m_PlayCtrl.realRound;

        /// <summary>
        /// 获取当前队列
        /// </summary>
        /// <returns></returns>
        public void GetTurnQueue(ref List<int> result)
        {
            result.AddRange(this.mQueue.Where(this.TurnPartCanUse).Select((x) => x.unit.GUID));
        }

#region TurnQueue 此部分为角色行动序列，常驻

        private List<TurnPart> mQueue = new List<TurnPart>();
        private Dictionary<Unit, TurnPart> mQueueDict = new Dictionary<Unit, TurnPart>();
        private TurnPart mCurrent;
        public TurnPart current => this.mCurrent;

#if UNITY_EDITOR
        public Dictionary<Unit, TurnPart> QueueDict => this.mQueueDict;
#endif

        private Predicate<TurnPart> mTurnPartCanUse;

        public void Initialize(BattlePlayController playCtrl)
        {
            this.mTurnPartCanUse = this.TurnPartCanUse;
            this.m_PlayCtrl = playCtrl;
            this.RefreshQueue();
        }

        public void RefreshQueue()
        {
            for (int i = 0; i < this.mQueue.Count; i++)
            {
                ClassPool.Put(this.mQueue[i]);
            }
            this.mQueue.Clear();
            this.mQueueDict.Clear();
            var rList = ListPool<Unit>.Get();
            for (int i = 0; i < this.m_PlayCtrl.unitCtrl.units.Count; i++)
            {
                var rUnit = this.m_PlayCtrl.unitCtrl.units[i];
                if (!rUnit.IsDead() && !rUnit.IsTotem())
                {
                    rList.Add(rUnit);
                }
            }
            rList.Sort((x, y) =>
            {
                var rAttrX = x.GetComponent<AttributeComponent>(ETComponentType.AttributeComponent);
                var rAttrY = y.GetComponent<AttributeComponent>(ETComponentType.AttributeComponent);
                return -rAttrX.Get(EAttributeType.Speed).CompareTo(rAttrY.Get(EAttributeType.Speed));
            });

            for (int i = 0; i < rList.Count; i++)
            {
                var rPart = ClassPool.Get<TurnPart>();
                rPart.unit = rList[i];
                rPart.executeRound = this.m_PlayCtrl.realRound - 1;
                rPart.curSkill = null;
                this.mQueue.Add(rPart);
                this.mQueueDict.TryAdd(rPart.unit, rPart);
            }
        }

        public bool TryNextTurn()
        {
            this.mCurrent = this.GetNextTurn();
            return this.mCurrent != null;
        }

        private TurnPart GetNextTurn()
        {
            this.mCurrent = this.mQueue.Find(this.mTurnPartCanUse);

            return this.mCurrent;
        }

        public void RemoveExtraTurn(Unit rUnit, int nCount)
        {
            if (this.mQueueDict.TryGetValue(rUnit, out var rTurn))
            {
                if (nCount > 0)
                {
                    rTurn.extraExecuteCount = Math.Max(0, rTurn.extraExecuteCount - nCount);
                }
                else
                {
                    rTurn.extraExecuteCount = 0;
                }
            }
        }

        public void AddExtraTurn(Unit rUnit, int nCount)
        {
            if (this.mQueueDict.TryGetValue(rUnit, out var rTurn) && nCount > 0)
            {
                rTurn.extraExecuteCount += nCount;
            }
        }

        private bool TurnPartCanUse(TurnPart turn)
        {
            if (turn == null || turn.unit == null)
            {
                return false;
            }
            if (turn.unit.Exists<DeadComponent>(ETComponentType.DeadComponent))
            {
                return false;
            }
            if (this.m_PlayCtrl.realRound == BattleConst.StrikeRound && turn.unit.teamIndex != this.m_PlayCtrl.strikeTeam)
            {
                return false;
            }
            if (turn.executeRound < this.RealRound)
            {
                turn.executeCount = 0;
            }
            if (turn.executeCount >= turn.maxExecuteCount && turn.extraExecuteCount <= 0)
            {
                return false;
            }
            var rCustom = turn.unit.GetComponent<CustomPropertyComponent>(ETComponentType.CustomPropertyComponent);
            return rCustom?.GetProperty<int>(ECustomProperty.ForbidTurn) <= 0;
        }

#endregion

#region SkillQueue 单个Turn中的行动序列 随时刷新

        private Queue<Skill> m_SkillQueue = new Queue<Skill>();
        public Queue<Skill> skillQueue => this.m_SkillQueue;

        public bool InsertSkill(Skill rSkill)
        {
            this.m_SkillQueue.Enqueue(rSkill);
            return true;
        }

        public void ClearSkill()
        {
            this.m_SkillQueue.Clear();
        }

#endregion

        protected override void OnDispose()
        {
        }

        public class TurnPart : IReusableClass
        {
            public Unit unit;
            public int executeRound = -1;
            public Skill curSkill;
            public int executeCount;
            public int maxExecuteCount = 1;
            public int extraExecuteCount = 0;

            public SelectParam selectParam { get; set; }

            public void Input(BattleSkillPOD battleSkillPod)
            {
                this.FreeInput();
                var rDic = this.unit.battle.playCtrl.unitCtrl.unitsMap;
                rDic.TryGetValue(battleSkillPod.targetId, out Unit rTargetUnit);

                var rInput = ClassPool.Get<SelectParam>();
                rInput.unit = rTargetUnit;
                rInput.pos = new LVector2((LFloat)battleSkillPod.targetPos.x, (LFloat)battleSkillPod.targetPos.y);
                rInput.aspect = new LVector2((LFloat)battleSkillPod.targetAspect.x, (LFloat)battleSkillPod.targetAspect.y);

                this.selectParam = rInput;
            }

            public void Input(LVector2 pos, LVector2 aspect)
            {
                this.FreeInput();

                var rInput = ClassPool.Get<SelectParam>();
                rInput.pos = pos;
                rInput.aspect = aspect;

                this.selectParam = rInput;
            }

            private void FreeInput()
            {
                if (this.selectParam != null)
                {
                    ClassPool.Put(this.selectParam);
                    this.selectParam = null;
                }
            }

            public uint MaxStore => 10;

            public void OnReset()
            {
                this.unit = default;
                this.executeRound = -1;
                this.curSkill = default;
                this.executeCount = default;
                this.maxExecuteCount = 1;
                this.extraExecuteCount = 0;
                this.selectParam = null;
            }

            public void OnTurnStart(int nRound)
            {
                this.executeRound = nRound;
                if (this.extraExecuteCount > 0)
                {
                    this.extraExecuteCount--;
                }
                else
                {
                    this.executeCount++;
                }
            }

            public LVector2 Execute()
            {
                if (this.curSkill != null)
                {
                    var rSkillComp = this.unit.GetComponent<SkillComponent>(ETComponentType.SkillComponent);
                    rSkillComp.OnTurnSkillStart();
                    var rTargetPos = this.selectParam.pos;
                    this.curSkill.Execute(this.selectParam);
                    return rTargetPos;
                }
                return LVector2.zero;
            }

            public void OnTurnEnd()
            {
                this.curSkill = null;
                this.FreeInput();
            }
        }
    }
}